/* -*- C++ -*- */
/* Copyright (C) 2002 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef _SP_PCONTEXT_H_
#define _SP_PCONTEXT_H_

#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif

typedef enum
{
  sp_param_in,
  sp_param_out,
  sp_param_inout
} sp_param_mode_t;

typedef struct sp_pvar
{
  LEX_STRING name;
  enum enum_field_types type;
  sp_param_mode_t mode;
  uint offset;  // Offset in current frame
  Item *dflt;
} sp_pvar_t;

#define SP_LAB_REF 0    // Unresolved reference (for goto)
#define SP_LAB_GOTO 1   // Free goto label
#define SP_LAB_BEGIN 2  // Label at BEGIN
#define SP_LAB_ITER 3   // Label at iteration control

typedef struct sp_label
{
  char *name;
  uint ip;           // Instruction index
  int type;          // begin/iter or ref/free
  sp_pcontext *ctx;  // The label's context
} sp_label_t;

typedef struct sp_cond_type
{
  enum
  {
    number,
    state,
    warning,
    notfound,
    exception
  } type;
  char sqlstate[6];
  uint mysqlerr;
} sp_cond_type_t;

/* Sanity check for SQLSTATEs. Will not check if it's really an existing
 * state (there are just too many), but will check length bad characters.
 */
extern bool sp_cond_check(LEX_STRING *sqlstate);

typedef struct sp_cond
{
  LEX_STRING name;
  sp_cond_type_t *val;
} sp_cond_t;

/*
  This seems to be an "SP parsing context" or something.
*/

class sp_pcontext : public Sql_alloc
{
  sp_pcontext(const sp_pcontext &); /* Prevent use of these */
  void operator=(sp_pcontext &);

 public:
  sp_pcontext(sp_pcontext *prev);

  // Free memory
  void destroy();

  sp_pcontext *push_context();

  // Returns the previous context, not the one we pop
  sp_pcontext *pop_context();

  sp_pcontext *parent_context() { return m_parent; }

  uint diff_handlers(sp_pcontext *ctx);

  uint diff_cursors(sp_pcontext *ctx);

  //
  // Parameters and variables
  //

  inline uint max_pvars() { return m_psubsize + m_pvar.elements; }

  inline uint current_pvars() { return m_poffset + m_pvar.elements; }

  inline uint context_pvars() { return m_pvar.elements; }

  inline uint pvar_context2index(uint i) { return m_poffset + i; }

  inline void set_type(uint i, enum enum_field_types type)
  {
    sp_pvar_t *p = find_pvar(i);

    if (p)
      p->type = type;
  }

  inline void set_default(uint i, Item *it)
  {
    sp_pvar_t *p = find_pvar(i);

    if (p)
      p->dflt = it;
  }

  void push_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);

  // Pop the last 'num' slots of the frame
  inline void pop_pvar(uint num = 1)
  {
    while (num--) pop_dynamic(&m_pvar);
  }

  // Find by name
  sp_pvar_t *find_pvar(LEX_STRING *name, my_bool scoped = 0);

  // Find by index
  sp_pvar_t *find_pvar(uint i)
  {
    sp_pvar_t *p;

    if (i < m_pvar.elements)
      get_dynamic(&m_pvar, (gptr)&p, i);
    else
      p = NULL;
    return p;
  }

  //
  // Labels
  //

  sp_label_t *push_label(char *name, uint ip);

  sp_label_t *find_label(char *name);

  inline sp_label_t *last_label()
  {
    sp_label_t *lab = m_label.head();

    if (!lab && m_parent)
      lab = m_parent->last_label();
    return lab;
  }

  inline sp_label_t *pop_label() { return m_label.pop(); }

  //
  // Conditions
  //

  void push_cond(LEX_STRING *name, sp_cond_type_t *val);

  inline void pop_cond(uint num)
  {
    while (num--) pop_dynamic(&m_cond);
  }

  sp_cond_type_t *find_cond(LEX_STRING *name, my_bool scoped = 0);

  //
  // Handlers
  //

  inline void push_handler(sp_cond_type_t *cond) { insert_dynamic(&m_handler, (gptr)&cond); }

  bool find_handler(sp_cond_type *cond);

  inline uint max_handlers() { return m_hsubsize + m_handlers; }

  inline void add_handlers(uint n) { m_handlers += n; }

  //
  // Cursors
  //

  void push_cursor(LEX_STRING *name);

  my_bool find_cursor(LEX_STRING *name, uint *poff, my_bool scoped = 0);

  inline uint max_cursors() { return m_csubsize + m_cursor.elements; }

  inline uint current_cursors() { return m_coffset + m_cursor.elements; }

 protected:
  // The maximum sub context's framesizes
  uint m_psubsize;
  uint m_csubsize;
  uint m_hsubsize;
  uint m_handlers;  // No. of handlers in this context

 private:
  sp_pcontext *m_parent;  // Parent context

  uint m_poffset;  // Variable offset for this context
  uint m_coffset;  // Cursor offset for this context

  DYNAMIC_ARRAY m_pvar;     // Parameters/variables
  DYNAMIC_ARRAY m_cond;     // Conditions
  DYNAMIC_ARRAY m_cursor;   // Cursors
  DYNAMIC_ARRAY m_handler;  // Handlers, for checking of duplicates

  List<sp_label_t> m_label;  // The label list

  List<sp_pcontext> m_children;  // Children contexts, used for destruction

};  // class sp_pcontext : public Sql_alloc

#endif /* _SP_PCONTEXT_H_ */
